package javasm3;
/**
 * SM3 Cryptographic Hash Algorithm  SM3密码杂凑算法
 * @author Jeen
 */
public class JavaSM3 
{
    private long msglen;//存储 字符 bit串长度
    private int klen; //存储 字符 bit串需填充的 0 的个数
    private static int T1 = 0x79cc4519; // Tj 0<= j <=15
    private static int T2 = 0x7a879d8a; // Tj 16<= j <=63
      
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) 
    {
        JavaSM3 $sm = new JavaSM3();
        StringBuilder teststr = new StringBuilder();
        
        //测试1 start
        //String msg0 = "abc";
        //teststr.append(msg0);  
        //测试1 数据构建 end
        
        //测试2 start
        int[] msg1 = {0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364,0x61626364};
        teststr = intsToString(msg1);
        //测试2 数据构建 end
        StringBuilder resultstr = new StringBuilder();
        int[] sm = new int[8];
        long startTime = System.currentTimeMillis();   //获取开始时间
        int i, n = 100000;
        for(i = 0; i < n; i++)
        {
            sm = $sm.getSM3(teststr.toString());
        }
        long endTime = System.currentTimeMillis(); //获取结束时间
        System.out.println(teststr.toString()+"执行加密运算"+n+"次时间： "+(endTime-startTime)+"ms");
        resultstr = intsToString(sm);
        System.out.println(resultstr);
        // TODO code application logic here
    }
    
    public int[] getSM3(String $message) //国密SM3 逻辑算法
    { 
        int[] $newmsg = getNewMsg($message);
        int gcount = (int)((msglen + klen + 65) / 512); //分组数量
        int[][] $bgroup = getNewMsgGroup($newmsg,gcount);
        int[][] $vgroup = new int[gcount + 1][8]; //用于迭代的二维数组
        int[] $tgroup = { 0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600, 0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e };//用于字寄存器初态
        int i = 0,j = 0; 
        for( i = 0; i < gcount; i++)
        {
            for(j = 0; (j < 8) && (i == 0); j++) //初始化寄存串
            {
                $vgroup[i][j] = $tgroup[j];
            }  
            $vgroup[i+1] = CF($vgroup[i],$bgroup[i],$tgroup);//迭代
            for(j = 0; (j < 8) && (gcount > 1); j++) //多次迭代修改寄存串
            {
                $tgroup[j] = $vgroup[i+1][j];
            }  
        }
        return $vgroup[gcount];
    }
    
    
    
    /*经过普通测试的私有加密逻辑方法*/
    private int[] CF(int[] $vi, int[] $bi, int[] $ti) //迭代压缩算法
    {
        int i, j, k, temp; //用于循环体及中间量存储
        //将bi扩展为132个字
        int[] wj = new int[68];
        int[] wj1 = new int[64]; 
        for(j = 0; j < 16; j++) 
            wj[j] = $bi[j]; 
        for(j = 16; j < 68; j++) 
        {
            wj[j] = P1(wj[j-16] ^ wj[j-9] ^ (Px(wj[j-3], 15))) ^ (Px(wj[j-13], 7)) ^ wj[j-6];
        }
        for(j = 0; j < 64; j++) 
        {
            wj1[j] = (wj[j] ^ wj[j+4]); 
        }
        int ss1,ss2,tt1,tt2,t;
        for(j = 0; j < 64; j++)
        {  //A B C D E F G H
          // 0 1 2 3 4 5 6 7
            if(j > 15) t = this.T2;
            else t = this.T1;
            ss1 = Px((Px($ti[0], 12) + $ti[4] + Px(t, j)) , 7);
            ss2 = (ss1 ^ Px($ti[0],12));
            tt1 = (int)(ff($ti[0], $ti[1], $ti[2], j) + $ti[3] + ss2 + wj1[j]);
            tt2 = (int)(gg($ti[4], $ti[5], $ti[6], j) + $ti[7] + ss1 + wj[j]);
            $ti[3] = $ti[2];
            $ti[2] = Px($ti[1], 9);
            $ti[1] = $ti[0];
            $ti[0] = tt1;
            $ti[7] = $ti[6];
            $ti[6] = Px($ti[5], 19);
            $ti[5] = $ti[4];
            $ti[4] = P0(tt2);
        }
        for(j = 0; j < 8; j++)
        {
            $vi[j] ^= $ti[j];
        }
        return $vi;
    }
    
    private int[][] getNewMsgGroup(int[] $msg,int n) //将新消息按 512bit 分组
    {
        int i,j,temp;
        int[][] group = new int[n][16];
        for(i = 0; i < n; i++)
        {
            temp = i * 16;
            for(j = 0; j < 16; j++)
                group[i][j] = $msg[j + temp];
        }
        return group;
    }
    
    private int[] getNewMsg(String $msg) //返回拼接后的消息 串
    {  
        int i, j, k, temp; //用于循环体及中间量存储
        byte[] msg = $msg.getBytes();
        msglen = ((long)msg.length) * 8; //msg 所占bit位长度
        temp = (int)((msglen + 1) % 512);
        klen = temp < 448 ? (448 - temp) : (512 - temp + 448) ; //需要填充0的个数
        int wordlen = (int)((msglen + 1 + klen + 64) / 32); //填充后的消息总字数 （1 字 = 4 字节 = 4 * 8 = 32 位）
        int[] newmsg = new int[wordlen]; //填充后的消息
        temp = 4 - msg.length % 4; //消息构成整数个字 需补充的 字节数
        k = msg.length + temp;//原有消息 填充后 构成的字 所包含的字节数
        for(i = 0; i < wordlen - 2; i ++)
        {
            //拼接消息串
            newmsg[i] = 0x00000000;
            temp = i * 4;
            if(temp < k)
            { 
                for( j = temp; j < temp+4; j++)
                {
                    newmsg[i] <<= 8;
                    if(j < msg.length)  
                        newmsg[i] |= msg[j]; 
                    else if(j == msg.length  && j <= k)  
                        newmsg[i] |= (0x1 << 7);  
                }
            }
            //拼接 1 及 k 个 0
            else
            {
                newmsg[i] = 0x00000000;
            }
        } 
        //拼接 64位 消息长度  
        temp = (int)(msglen >>> 32);
        newmsg[wordlen-2] = temp;
        temp = (int)((msglen << 32) >>> 32);
        newmsg[wordlen-1] = temp; 
        //完成拼接
        return newmsg;
    }
    
    /*一些通用静态方法*/
    private static StringBuilder intsToString(int[] tint) //将用整型寄存的消息串 拼接 为字符串
    {
        int i;
        char[] t = new char[4];
        StringBuilder st = new StringBuilder();
        for(i = 0 ; i < tint.length; i++)
        { 
            t[0] = (char)(tint[i] >>> 24);
            t[1] = (char)((tint[i] << 8 ) >>> 24);
            t[2] = (char)((tint[i] << 16 ) >>> 24);
            t[3] = (char)((tint[i] << 24 ) >>> 24);
            st.append(t);
        }
        return st;
    }
    private static int Px(int x,int n) //整型循环左移运算 n <=32 //java中没有无符号整型,需要注意移位后的填充符
    { //忽略容错及特殊情况判断
        //if(n > 32) n = n % 32;
        //if(n == 0) return x;
        return ((x<<n)|(x>>>(32-n)));//fuck the sign bit
    }
    private static int P0(int x) //置换函数
    {  
        return x ^ (Px(x, 9)) ^ (Px(x, 17));
    }
    private static int P1(int x) //置换函数
    { 
        return x ^ (Px(x, 15)) ^ (Px(x, 23));
    }
    private static long ff(int x,int y,int z,int j) //使用long 解决 Java 欠缺中无符号整型 导致的异常
    { 
        long a = (long)x;
        long b = (long)y;
        long c = (long)z;
        if(j>15) 
        {  
            return ((a&b)|(a&c)|(b&c));
        }
        else 
        { 
            return a^b^c;
        }
    }
    private static long gg(int x,int y,int z,int j) //使用long 解决 Java 欠缺中无符号整型 导致的异常
    { 
        long a = (long)x;
        long b = (long)y;
        long c = (long)z;
        if(j > 15) 
        { 
            return ((a&b)|(~a&c));
        }
        else 
        { 
            return a^b^c;
        }
    }
}
